procedure main(args)
local delay, raw, where, localindex, noindex
«initialization»
every braw | eraw := ""
delay := !args == "-delay"
noindex := !args == "-noindex"
localindex := (/noindex, !args == "-localindex") # produce local identifier xref?
raw := !args == "-raw"
if then braw := "
beginrawhtml"; eraw := "
endrawhtml"
while inputline := read() do inputline ?
«scan and convert»
write()
end
«scan and convert»=
«@text»
«@nl»
«code chunks»
«@defn»
«docs chunks»
«@use»
«@xref»
«@index»
«others»
if ="@" then # follows last else
warn_unknown(1(tab(upto(' ')|0), pos(0) | move(1)))
else
write(&errout, "Botched line in noweb pipeline: ", tab(0))
@
[[ecode]] is the marker used at the end of the current code chunk.
If there is no cross-reference stuff at the end, we just use [[</pre>]];
otherwise we terminate whatever environment is used for the cross-reference stuff.
«code chunks»=
if ="@begin code " then code := 1 ; writes(braw, "<pre>"); ecode := "</pre>" else
if ="@end code " then code := nil ; previscode := 1
«dump pending cross-reference info»
writes(ecode, eraw) else
@
We want to try to avoid emitting paragraph elements when the
preceding chunk is a code chunk, as tracked by [[previscode]].
Also, if we do slip in a paragraph, we may use the LATEX style.
«docs chunks»=
if ="@begin docs " then if then writes(if /raw then "<p>" else "
par")
previscode := &null
text := 0 else
@
Sometimes it happens that a document-chunk anchor is put in a document chunk that
contains no text. In that case, we put in a phony anchor at the end of the chunk so
we won't lose the cross-reference.
«docs chunks»=
if ="@end docs " then write(braw, linklabel(, "*"), eraw)
lastxreflabel := &null else
@
Normally, if there's a pending anchor, we put it on the first available text line.
There's a bit of a fine point that crops up if the very first piece of text
is quoted code. In that case we have to attach both the label for the
docs anchor and the ref for the index anchor.
«@text»=
if ="@text " then text +:= *(line := tab(0))
if then
writes((«index anchor») | escapeSpecials(line))
else if then
writes((«index and docs anchor») | escapeSpecials(line))
else
writes((«docs anchor») | line) else
«index anchor»=
2(line ? tab(many(' '͡)); not pos(0),
linkto(, escapeSpecials(line)),
lastindexref := &null)
@
We anchor on the first nonblank character of the line, unless that's
an SGML tag, in which case we have to
try to skip past. If it's an anchor tag, we give up, just
inserting a * to anchor to.
None of this crap would be necessary if HTML could anchor to empty text.
«docs anchor»=
2(line ? tab(many(' '͡)); not pos(0),
line ? braw || linklabel(,
(tab(many(' '͡)) | "") ||
(skip_tags_and_char() | "*")) ||
eraw || tab(0),
insert(defns_above, ),
lastxreflabel := &null)
@
An indexed identifier is not empty and contains no tags, so we don't
have to worry.
«index and docs anchor»=
2(line ? tab(many(' '͡)); not pos(0),
linklabelto(lastxreflabel, lastindexref, escapeSpecials(line)),
lastxreflabel := lastindexref := &null)
@
Skip as many tags as possible, then skip a character, but
never skip an anchor ([[<a>]]) tag, because that might mess things up big time.
Argument is number of tags already skipped; if nonzero, we're willing to succeed
at the end of the string.
«*»=
procedure skip_tags_and_char(count)
local tag
/count := 0
if any('&') & upto(';') then
suspend(tab(upto(';')) || =";")
else if any('<') then
suspend ="<" || (tab(many(' '͡)) | "") ||
(="/" | (tag := tab(many(&letters)), map(tag) == "a", tag)) ||
tab(upto(">")) || =">" || skip_tags_and_char(count+1)
else
suspend (tab(many(' '͡)) | "") ||
((move(1) | if count > 0 then "")) # succeed at end if count > 0
end @ The preceding scheme sometimes wraps an anchor around a tag. If that turns out to be bad, we could try the following alternative: «funky new docs anchor»= 2(line ? tab(many(' '͡)); not pos(0), line ? braw || scan_initial_tags() || linklabel(, (tab(many(' '͡)) | "") || (scan_past_char() | "*")) || eraw || tab(0), insert(defns_above, ), lastxreflabel := &null) «procedures to support new docs anchor»= procedure scan_initial_tags() suspend (="<" || (tab(many(' '͡)) | "") || (="/" | (tag := tab(many(&letters)), map(tag) == "a", tag)) || tab(upto(">")) || =">" || scan_initial_tags()) | "" end
procedure scan_past_char()
if any('&') & upto(';') then
suspend(tab(upto(';')) || =";")
else
suspend (tab(many(' '͡)) | "") || tab(any( '<'))
end
«@nl»=
if ="@nl" & pos(0) then write() else
«@defn»=
if ="@defn " then writechunk(lastxreflabel, lastxrefref, "dfn",
thischunk := tab(0), defns[thischunk] || "=")
insert(defns_above, )
«clear [[lastxref*]]»
defns[thischunk] := "+" else
«initialization»=
defns := table("")
defns_above := set() # keep track of defining chunks we've seen
«@use»=
if ="@use " then writechunk(lastxreflabel, lastxrefref, "i", tab(0))
«clear [[lastxref*]]» else
@
Writing a chunk involves creating an anchor for it.
«*»=
procedure writechunk(label, ref, tag, name, suffix)
/suffix := ""
writes(linklabelto(label, ref,
sgmlwrap(tag, "<" || convquotes(name) || ">" || suffix)))
return
end
@
«others»=
if ="@quote" & pos(0) then quoting := 1 ; writes(braw, "<code>") else
if ="@endquote" & pos(0) then quoting := nil ; writes("</code>", eraw) else
«others»=
if ="@file " then filename := tab(0); «clear [[lastxref*]]» else
if ="@literal " then writes(tab(0)) else
if ="@header html " then «write HTML header» else
if ="@trailer html" & pos(0) then «write HTML trailer» else
@
«write HTML header»=
writes("<html><head><title>", tab(0), "</title></head><body>")
«write HTML trailer»=
write("</body></html>")
@
«@xref»=
if ="@xref " then
if fun := tab(upto(' ')) then move(1); arg := tab(0)
else fun := tab(0); arg := &null
case fun of
«cases for @xref»
default : arg ? warn_unknown("xref " || fun)
else
«cases for @xref»=
"label" : «warn if unused [[lastxreflabel]]»; lastxreflabel := arg
"ref" : «warn if unused [[lastxrefref]]»; lastxrefref := arg
"prevdef" : pendingprev := arg
"nextdef" : pendingnext := arg
"beginuses" : useitems := []
"useitem" : put(useitems, arg)
"enduses" : useitemstab[thischunk] := useitems
"notused" : «code-to-blockquote»
writes("This code is written to a file (or else not used).<p>")
«initialization»=
useitemstab := table()
«clear [[lastxref*]]»=
every lastxreflabel | lastxrefref := &null
«warn if unused [[lastxreflabel]]»=
warn_unused_xref("label", )
«warn if unused [[lastxrefref]]»=
warn_unused_xref("ref", )
«*»=
procedure warn_unused_xref(tag, label)
static warned
initial warned := set()
if not member(warned, tag) then
insert(warned, tag)
write(&errout, "Warning: internal inconsistency in noweb (not urgent)—")
write(&errout, "used @xref ", tag, " ", label)
return
end
@
«dump pending cross-reference info»=
useitems := useitemstab[thischunk]
if | | *> 0 then
«code-to-blockquote»
«write out uses with links»
if *> 0 & (| ) then
writes("; ")
p := if *> 0 then "previous" else "Previous"
n := if *> 0 then "next" else "Next"
if then
if then
writes(linkto(pendingprev, p), " and ",
linkto(pendingnext, "next"), " definitions")
else
writes(linkto(pendingprev, p), " definition")
else
if then
writes(linkto(pendingnext, n), " definition")
pendingprev := pendingnext := &null
useitems := &null
write(".<p>")
«write out uses with links»=
useprefix := "Used "
every i := 1 to *do
usedir := if member(defns_above, useitems[i]) then "above" else "below"
usesuffix := if *> 1 then " (" || i || ")" else ""
writes(useprefix, linkto(useitems[i], usedir || usesuffix))
useprefix := ", "
@
The hack here is to put the supplementary information in a blockquote area
after the code.
«code-to-blockquote»=
if ecode == "</pre>" then
writes("</pre><blockquote>")
ecode := "</blockquote>"
@
The HTML back end ignores [[@xref begindefs]], [[@xref defitem]], and
[[@xref enddefs]]; it uses the [[nextdef]] and [[prevdef]] links instead.
«cases for @xref»=
"begindefs" | "defitem" | "enddefs" : &null
@
«cases for @xref»=
"beginchunks" : write(braw, "<ul>")
"chunkbegin" : writes("<li>"); comma := ": "; count := 0
arg ? ref := tab(upto(' ')); =" "; name := tab(0)
writechunk(&null, ref, "i", name)
"chunkuse" : writes(comma, linkto(arg, "U" || (count +:= 1))); comma := ", "
"chunkdefn" : writes(comma, linkto(arg, "D" || (count +:= 1))); comma := ", "
"chunkend" : write()
"endchunks" : write("</ul>", eraw)
«cases for @index»=
"beginindex" : write(braw, "<ul>")
"entrybegin" : writes("<li>"); comma := ": "; count := 0
arg ? ref := tab(upto(' ')); =" "; name := tab(0)
writes(linklabelto("NWI-" || escapeSpecials(name), ref, name))
"entryuse" : writes(comma, linkto(arg, "U" || (count +:= 1))); comma := ", "
"entrydefn" : writes(comma, linkto(arg, "D" || (count +:= 1))); comma := ", "
"entryend" : write()
"endindex" : write("</ul>", eraw)
«@index»=
if ="@index " then
if /noindex then
if fun := tab(upto(' ')) then move(1); arg := tab(0)
else fun := tab(0); arg := &null
case fun of
«cases for @index»
default : arg ? warn_unknown("index " || fun)
# don't get any warnings if not doing indexing
else
@
The local identifier cross-reference doesn't show each use; it just shows
the identifiers that are defined, with links to the full index.
«cases for @index»=
"use" : lastindexref := lastxrefref; lastxrefref := &null
"defn" : «clear [[lastxref*]]»
"localdefn" : «clear [[lastxref*]]»
"nl" : &null # do nothing – destroys line numbering
"begindefs" : if then
«code-to-blockquote»; writes("Defines"); comma := " "
"isused" : &null
"defitem" : if then
writes(comma, linkto("NWI-" || escapeSpecials(arg),
sgmlwrap("code", escapeSpecials(arg))))
comma := ", "
"enddefs" : if then write(" (links are to index).<p>")
"beginuses" | "isdefined" | "useitem" | "enduses" : &null # use local links
@